﻿/*
* UDPdriver.cpp
* Author: Zhong Ming
* Data: 20171016
*/

#ifdef _WIN32

#include <assert.h>
#include <Winsock2.h>
#include "UDPdriver.h"
#include <QDebug>

#pragma comment(lib, "ws2_32.lib")

struct CUDPdriver::CUDPdriver_priv
{
	enum 
	{ 
		udp_listen_mode, 
		udp_connect_mode, 
		udp_idle_mode 
	} current_mode;

	bool isRemoteConnected;
	struct sockaddr_in local_fd;
	struct sockaddr_in remote_fd;
	SOCKET udp_fd;
};

CUDPdriver::CUDPdriver(void)
{
	priv = new CUDPdriver_priv;
	priv->isRemoteConnected = false;
	priv->current_mode = CUDPdriver_priv::udp_idle_mode;	

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 1), &wsaData))
	{
		perror("Winsock Socket Initialize Failed");
		assert("Cannot Initialize Winsock\n");
	}

	priv->udp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	if (priv->udp_fd == INVALID_SOCKET)
	{
		perror("UDP Socket Create Failed");
		assert("UDP Socket Create Failed");
	}	

/*****************************************************************************/
// 2017 11 03  王洋洋

	int err = -1;		/* 返回值 */
	int snd_size = 0;	/* 发送缓冲区大小 */
	int rcv_size = 0;	/* 接收缓冲区大小 */
	int optlen;			/* 选项值长度 */

	/*
	* 设置发送缓冲区大小
	*/
	snd_size = 64 * 1024; /* 发送缓冲区大小为8K */
	optlen = sizeof(snd_size);
	err = setsockopt(priv->udp_fd, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, optlen);
	if (err<0){
		printf("设置发送缓冲区大小错误\n");
	}

	/*
	* 设置接收缓冲区大小
	*/
	rcv_size = 64 * 1024; /* 接收缓冲区大小为8K */
	optlen = sizeof(rcv_size);
	err = setsockopt(priv->udp_fd, SOL_SOCKET, SO_RCVBUF, (char *)&rcv_size, optlen);
	if (err<0){
		printf("设置接收缓冲区大小错误\n");
	}
	
	/*
	* 先读取缓冲区设置的情况
	* 获得原始发送缓冲区大小
	*/
	optlen = sizeof(snd_size);
	err = getsockopt(priv->udp_fd, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &optlen);
	if (err<0){
		printf("获取发送缓冲区大小错误\n");
	}
	/*
	* 打印原始缓冲区设置情况
	*/

	/*
	* 获得原始接收缓冲区大小
	*/
	optlen = sizeof(rcv_size);
	err = getsockopt(priv->udp_fd, SOL_SOCKET, SO_RCVBUF, (char *)&rcv_size, &optlen);
	if (err<0){
		printf("获取接收缓冲区大小错误\n");
	}

    qDebug(" 发送缓冲区原始大小为: %d 字节\n", snd_size);
    qDebug(" 接收缓冲区原始大小为: %d 字节\n", rcv_size);
/******************************************************************************/

}

CUDPdriver::~CUDPdriver(void)
{
	if (priv->current_mode == CUDPdriver_priv::udp_connect_mode)
	{
		closesocket(priv->udp_fd);
	}
	else if (priv->current_mode == CUDPdriver_priv::udp_listen_mode)
	{
		closesocket(priv->udp_fd);
	}
	delete priv;

	WSACleanup();
}

int CUDPdriver::listen( unsigned int local_port_input, void* options_input /*= NULL*/ )
{
	priv->local_fd.sin_family = AF_INET;
	priv->local_fd.sin_port = htons(local_port_input);
	priv->local_fd.sin_addr.s_addr = INADDR_ANY;

	int ret = bind(priv->udp_fd, (SOCKADDR*)&priv->local_fd, sizeof(SOCKADDR));
	if (ret < 0)
	{
		perror("UDP Listen Bind Failed");
	}

	return ret;
}

int CUDPdriver::connect( char* remote_ip_input, unsigned int remote_port_input, unsigned int local_port_input, void* options_input /*= NULL*/ )
{
	int ret = 0;

	priv->remote_fd.sin_family = AF_INET;
	priv->remote_fd.sin_port = htons(remote_port_input);
	priv->remote_fd.sin_addr.s_addr = inet_addr(remote_ip_input);
	priv->isRemoteConnected = true;

	priv->local_fd.sin_family = AF_INET;
	priv->local_fd.sin_port = htons(local_port_input);
	priv->local_fd.sin_addr.s_addr = INADDR_ANY;

	ret = bind(priv->udp_fd, (SOCKADDR*)&priv->local_fd, sizeof(SOCKADDR));
	if (ret < 0)
	{
		perror("UDP Listen Bind Failed");
	}
	
	return ret;
}

int CUDPdriver::connect( char* remote_ip_input, unsigned int remote_port_input, void* options_input /*= NULL*/ )
{
	int ret;

	ret = connect(remote_ip_input, remote_port_input, 0, options_input);

	return ret;
}

int CUDPdriver::send_data( void* buf, unsigned int bytes )
{
	int ret;

	if (priv->isRemoteConnected == false)
	{
		printf("Error: Remote port is not specified!\n");
		return -1;
	}
	
	ret = sendto(priv->udp_fd, (const char*)buf, bytes, 0, (struct sockaddr*)&priv->remote_fd, sizeof(struct sockaddr));

	return ret;
}

int CUDPdriver::send_data_with_ip(void* buf, unsigned int bytes, char* remote_ip, unsigned int remote_port)
{
	int ret;
	struct sockaddr_in tmp_remote_fd;

	if (priv->isRemoteConnected == false)
	{
		printf("Error: Remote port is not specified!\n");
		return -1;
	}

	tmp_remote_fd.sin_family = AF_INET;
	tmp_remote_fd.sin_port = htons(remote_port);
	tmp_remote_fd.sin_addr.s_addr = inet_addr(remote_ip);

	ret = sendto(priv->udp_fd, (const char*)buf, bytes, 0, (struct sockaddr*)&tmp_remote_fd, sizeof(struct sockaddr));

	return ret;
}

int CUDPdriver::recv_data(void* buffer, unsigned int bytes)
{
	SOCKADDR_IN  remote_info;
	int length = sizeof(SOCKADDR);
	int ret;

	memset(&remote_info, 0x00, length);
		
	ret = recvfrom(priv->udp_fd, (char*)buffer, bytes, 0, (SOCKADDR*)&remote_info, &length);

	if (ret > 0)
	{
		priv->remote_fd.sin_family = AF_INET;
		priv->remote_fd.sin_port = remote_info.sin_port;
		priv->remote_fd.sin_addr = remote_info.sin_addr;
		priv->isRemoteConnected = true;
	}
	else
	{
		perror("UDP Recv Failed");
	}

	return ret;
}

int CUDPdriver::recv_data_with_ip(UDPdriver_Data *data)
{
	SOCKADDR_IN  remote_info;
	int length = sizeof(SOCKADDR);
	int ret;

	memset(&remote_info, 0x00, length);
	ret = recvfrom(priv->udp_fd, (char*)data->buffer, data->bytes, 0, (SOCKADDR*)&remote_info, &length);

	if (ret > 0)
	{
		priv->remote_fd.sin_family = AF_INET;
		priv->remote_fd.sin_port = remote_info.sin_port;
		priv->remote_fd.sin_addr = remote_info.sin_addr;
		priv->isRemoteConnected = true;

		strcpy(data->remote_ip, inet_ntoa(remote_info.sin_addr));
		strcpy(data->local_ip, inet_ntoa(priv->local_fd.sin_addr));
		data->remote_port = htons(remote_info.sin_port);
		data->local_port = htons(priv->local_fd.sin_port);
	}
	else
	{
		perror("UDP Recv Failed");
	}
	
	return ret;
}

#else

// TODO: Linux version to be done

#endif
